package util

import (
	"crypto/tls"
	"fmt"
	"github.com/Wanghongw/unpackit"
	"github.com/hashicorp/go-retryablehttp"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
	"unpackitDemo/model"
)

const (
	GZIPS_PATH     = "./gzs/"
	ConnectTimeout = 10 * time.Second
	RequestTimeout = 30 * time.Second
)

// 根据url获取GZ压缩文件的文件名
func GetGZNameFromURL(url string) (fileName string, err error) {
	// TODO：第一种返回错误的方式：构建一个结构体对象
	if url == "" {
		errObj := model.ErrOut{
			Where:  "getFileNameFromURL",
			Detail: "url不能为空",
		}
		return fileName, &errObj
	}
	currLst := strings.Split(url, "/")
	fileName = currLst[len(currLst)-1]
	return fileName, nil
}

// 将指定目录删除，包括目录中所有的文件
func DeleteFilePath(filePath string) error {
	if err := os.RemoveAll(filePath); err != nil {
		return model.NewError("deleteFilePath", err.Error())
	}
	return nil
}

// 方法1：现在本地生成一个gz压缩包，再解压，处理解压之后的文件。

// 将数据写入指定目录的文件中
func WriteDataToFile(fileName string, data []byte) error {
	// 创建目录 需要检查一下目录是否存在！
	if err := os.Mkdir(GZIPS_PATH, 0777); os.IsExist(err) == true {
		fmt.Println("目录已经存在了！")
	}
	// 文件不存在会创建文件
	filePath := GZIPS_PATH + fileName
	if err := ioutil.WriteFile(filePath, data, os.ModePerm); err != nil {
		// TODO：第二种返回错误的方式：使用工厂函数
		return model.NewError("writeDataToFile", err.Error())
	}
	return nil
}

// 解压gz文件
func DecompressionGZ(fileName string) (destPath string, err error) {
	filePath := GZIPS_PATH + fileName
	// 将最后的 .gz 后缀去掉
	newFileName := fileName[:(len(fileName) - 3)]
	file, err1 := os.Open(filePath)
	if err1 != nil {
		return "", model.NewError("decompressionGZ.osOpen", err1.Error())
	}
	destPath, err = unpackit.Unpack(file, GZIPS_PATH, newFileName, 500*1024)
	if err != nil {
		return "", model.NewError("decompressionGZ.unpack", err.Error())
	}
	return destPath, nil
}

// 方法2：直接根据request.Body的字节流数据解压文件，处理解压之后的文件。
func DecompressionReqBody(fileName string, body io.Reader) (string, error) {
	newFileName := fileName[:(len(fileName) - 3)]
	destPath, err := unpackit.Unpack(body, GZIPS_PATH, newFileName, 500*1024)
	if err != nil {
		return "", model.NewError("decompressionReqBody", err.Error())
	}
	return destPath, nil
}

// 使用http连接池
func NewTransport() *http.Transport {
	dialContext := (&net.Dialer{
		Timeout:   ConnectTimeout,
		KeepAlive: 30 * time.Second,
	}).DialContext

	return &http.Transport{
		Proxy:                 http.ProxyFromEnvironment,
		DialContext:           dialContext,
		MaxIdleConns:          100,
		MaxConnsPerHost:       25,
		IdleConnTimeout:       30 * time.Second,
		TLSHandshakeTimeout:   ConnectTimeout,
		ExpectContinueTimeout: 1 * time.Second,
	}
}

func NewRetryHttpClient() *retryablehttp.Client {
	retryClient := retryablehttp.NewClient()
	retryClient.RetryMax = 10
	retryClient.Logger = nil
	return retryClient
}

// 连接池字典
var clientMap = make(map[string]*retryablehttp.Client)

// 带证书认证的结构
var transportMap = make(map[string]*http.Transport)

// 加一个锁 防止多线程同时写入字典的情况
var muClient sync.Mutex

func GetHttpClient(tag string, config *tls.Config) *http.Client {
	muClient.Lock()
	defer muClient.Unlock()
	// 如果是一个带证书的请求，在这里处理
	if config != nil {
		if _, ok := transportMap[tag]; !ok {
			transportMap[tag] = NewTransport()
			transportMap[tag].TLSClientConfig = config
		}
		return &http.Client{
			Transport: transportMap[tag],
			Timeout:   RequestTimeout,
		}
	}
	if _, ok := clientMap[tag]; !ok {
		clientMap[tag] = NewRetryHttpClient()
	}
	return clientMap[tag].StandardClient()
}
